package com.common.android.utils.http;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Map;

import javax.net.ssl.SSLHandshakeException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.NoHttpResponseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.params.ConnManagerParams;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.ExecutionContext;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.json.JSONObject;

import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;

import com.common.android.utils.MD5ArithmeticUtils;
import com.nd.android.u.allcommon.R;
import com.product.android.business.ApplicationVariable;
import com.product.android.utils.LogUtils;

/**
 * Wrap of org.apache.http.impl.client.DefaultHttpClient
 * @author lds
 */
public class HttpBaseClient {

    protected static final String TAG = "HttpBaseClient";
    protected final static String UTF8="UTF-8";

    /** 服务端响应成功 */
    public static final int OK = 200;
    public static final int RESULT_OK =204;    
    /** 注册成功*/
    public static final int REGISTER_OK = 201;
    /** 服务端响应成功，但是无返回数据 */
    public static final int NOT_RESULT = 204;
    /** 输入不合法，提交的数据不是json 格式时，返回此状态码 */
    public static final int IPNUT_DATA_ERROR = 400;
    /** 清有请求的权限 */
    public static final int NOT_AUTHORIZED = 401;    
    public static final int AUTH_ERROR =403;    
    public static final int ERROR_404 = 404;
    /** 请求的方法不存在 */
    public static final int METHOND_NOT_FOUND = 405;
    /** 接口未实现 */
    public static final int INTERFACE_NOT_IMPLEMENTS = 501;
    //连接时间超时
    protected static final int CONNECTION_TIMEOUT_MS = 20 * 1000;
    //SOCKET连接时间超时
    protected static final int SOCKET_TIMEOUT_MS = 60 * 1000;
    //重新得到上限20
    public static final int RETRIEVE_LIMIT = 20;
    //重试时间3
    public static final int RETRIED_TIME = 0;
    protected DefaultHttpClient mClient;
    protected BasicHttpContext localcontext;
 
    protected String sid = "";
  
    public HttpBaseClient() {
//    	 mClient = new DefaultHttpClient();
        prepareHttpClient();
    }
    /**
     * 增加一个传参构造，区别于无参构造，获取支持https的client时用
     * Creates a new instance of HttpClient.
     * <br>Created 2014-9-25 上午9:55:36
     * @param httpsable
     */
    public HttpBaseClient(int httpsable) {
//   	 mClient = new DefaultHttpClient();
       prepareHttpClient(true);
   }
    
	/**
     * 设置代理
     * @param hostname the hostname (IP or DNS name)主机名
     * @param port the port number. -1 indicates the scheme default port.端端口
     * @param scheme  the name of the scheme. null indicates the default scheme
     */
    public void setProxy(String host, int port, String scheme) {
//        HttpHost proxy = new HttpHost(host, port, scheme);  
//      mClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
    }
    
    /**
     * 移除代理
     */
    public void removeProxy() {
//       mClient.getParams().removeParameter(ConnRoutePNames.DEFAULT_PROXY);
    }

    /**
     * 
     * 获取缺省的client(只支持http请求)
     * <br>Created 2014-9-25 上午9:53:05
     * @author       jiaoyun
     */
    protected void prepareHttpClient() {
// 		  Create and initialize HTTP parameters  表示HTTP协议和框架参数集合
//        HttpParams params = new BasicHttpParams();
//        //设置每个连接最大的自然连接数
//        ConnManagerParams.setMaxTotalConnections(params, 100);
//        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
//        SchemeRegistry schemeRegistry = new SchemeRegistry();
//        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
//        schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
        mClient = new DefaultHttpClient();
//        new AuthScope(UAPConfiguration.getUAPServiceURL(), AuthScope.ANY_PORT);
//        mClient.setCredentialsProvider(new BasicCredentialsProvider());       
//        BasicScheme basicScheme = new BasicScheme();
//        localcontext = new BasicHttpContext();
//        localcontext.setAttribute("preemptive-auth", basicScheme);
    }
    /**
     * 获取支持https请求的client
     * 
     * <br>Created 2014-9-25 上午9:53:59
     * @param httpsable
     * @author       jiaoyun
     */
    protected void prepareHttpClient(boolean httpsable) {
		try {
	        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
	        trustStore.load(null, null); 
	        SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);  
	        sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 
	        HttpParams params = new BasicHttpParams();

//	        //设置每个连接最大的自然连接数
	        ConnManagerParams.setMaxTotalConnections(params, 100);
	        HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
	    	HttpProtocolParams.setContentCharset(params,
					HTTP.UTF_8);
			HttpProtocolParams.setUseExpectContinue(params, true);
			HttpConnectionParams.setConnectionTimeout(params,
					CONNECTION_TIMEOUT_MS);
			HttpConnectionParams.setSoTimeout(params, SOCKET_TIMEOUT_MS);
	        SchemeRegistry schemeRegistry = new SchemeRegistry();
	        schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
	        schemeRegistry.register(new Scheme("https", sf, 443));
	        ClientConnectionManager conManager = new ThreadSafeClientConnManager(
					params, schemeRegistry);
	        mClient = new DefaultHttpClient(conManager, params);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			mClient = new DefaultHttpClient();
		} 
    }
    
    public Response httpRequest(String url, JSONObject jsonobject,  String httpMethod,File file,ArrayList<BasicNameValuePair> postParams,CookieStore cookieStore) throws HttpAuthException,HttpServerException, HttpException   {    	
        return httpRequest(url,jsonobject,httpMethod,null);
    }
    
    public Response httpRequest(String url,  String httpMethod,Map<String,String> map) throws HttpAuthException,HttpServerException, HttpException   {    	
        return httpRequest(url,null,httpMethod,map);
    }
    
    protected HttpUriRequest createMethod(String httpMethod, URI uri,JSONObject jsonobject,File file,ArrayList<BasicNameValuePair> postParams) throws HttpException {
        return createMethod(httpMethod,uri,jsonobject,null);
    }
    
    protected HttpUriRequest createMethod(String httpMethod, URI uri,Map<String,String> map) throws HttpException {
        return createMethod(httpMethod,uri,null,map);
    }
    
    public Response httpRequest(String url, JSONObject jsonobject,  String httpMethod) throws HttpAuthException,HttpServerException, HttpException   {
    	return httpRequest(url,jsonobject,httpMethod,null);
    }

    public Response httpRequest(String url, JSONObject jsonobject,  String httpMethod,Map<String,String> map) throws HttpAuthException,HttpServerException, HttpException   {    	
        URI uri = createURI(url);
//        Log.d(TAG, "Sending1 " + httpMethod + " request to " + url);
        HttpResponse response = null;
        Response res = null;
        HttpUriRequest method = null;
        method = createMethod(httpMethod, uri, jsonobject,map);
        setupHTTPConnectionParams(method);
        try {
            response = mClient.execute(method,localcontext);    
            res = new Response(response);
            
        } catch (SocketTimeoutException nte) {
            throw new HttpException(nte.getMessage(), HttpStatus.SC_CONFLICT);
        }catch (ClientProtocolException e) {
            throw new HttpException(e.getMessage(), e);
        } catch (IOException ioe) {
            throw new HttpException(ioe.getMessage(), ioe);
        }
        catch(RuntimeException e){
        	LogUtils.e(TAG," httpRequest throw runtimeException");
        }
        if (response != null) {
            int statusCode = response.getStatusLine().getStatusCode();
            handleResponseStatusCode(statusCode, res);
        } else {
            throw new HttpException("response is null");
        }        
        return res;
    }
    
    public Response httpRequest(String url, byte[] b,JSONObject json,String httpMethod) throws HttpAuthException,HttpServerException, HttpException   {
    	  URI uri = createURI(url);
//          Log.d(TAG, "Sending " + httpMethod + " request to " + url);
          HttpResponse response = null;
          Response res = null;
          HttpUriRequest method = null;
          method = createMethod(httpMethod, uri, b);
          setupHTTPConnectionParams(method);
          try {
              response = mClient.execute(method,localcontext);    
              res = new Response(response);
              
          } catch (SocketTimeoutException nte) {
              throw new HttpException(nte.getMessage(), HttpStatus.SC_CONFLICT);
          }catch (ClientProtocolException e) {
              throw new HttpException(e.getMessage(), e);
          } catch (IOException ioe) {
              throw new HttpException(ioe.getMessage(), ioe);
          }
          catch(RuntimeException e){
          	LogUtils.e(TAG," httpRequest throw runtimeException");
          }
          if (response != null) {
              int statusCode = response.getStatusLine().getStatusCode();
              handleResponseStatusCode(statusCode, res);
          } else {
              throw new HttpException("response is null");
          }        
          return res;
    }
    

    /**
     * CreateURI from URL string
     * 创建URI
     * @param url
     * @return request URI
     * @throws HttpException
     * Cause by URISyntaxException
     */
    protected URI createURI(String url) throws HttpException {
        URI uri;
        try {
            uri = new URI(url);
        } catch (URISyntaxException e) {
            LogUtils.e(TAG, e.getMessage(), e);
            throw new HttpException("Invalid URL.");
        }
        return uri;
    }

    /**
     * Setup HTTPConncetionParams
     * 设置HTTP的连接参数,超时时间，重试，异常处理
     */
    protected void setupHTTPConnectionParams(HttpUriRequest method) {
        HttpConnectionParams.setConnectionTimeout(method.getParams(),CONNECTION_TIMEOUT_MS);
        HttpConnectionParams.setSoTimeout(method.getParams(), SOCKET_TIMEOUT_MS);
        //设置异常自动处理
//        mClient.setHttpRequestRetryHandler(requestRetryHandler);        
        method.addHeader("Accept-Encoding", "gzip, deflate");
        if (sid != null && !sid.equals("null")) {
            method.addHeader(new BasicHeader("COOKIE", "PHPSESSID="	+ sid + ""));
		} else {
	        method.addHeader(new BasicHeader("COOKIE", "PHPSESSID="	+ ""));
		}
    }
    
    /*
     * 添加byte
     */
    protected HttpUriRequest createMethod(String httpMethod, URI uri,byte[] b) throws HttpException {
        HttpUriRequest method;
        //HttpPost
        if (httpMethod.equalsIgnoreCase(HttpPost.METHOD_NAME)) {
            HttpPost post = new HttpPost(uri);
            try {
	            if(b!=null){
	            	ByteArrayEntity byteArray = new ByteArrayEntity(b);
	            	post.setEntity(byteArray);
	            }
            }catch (Exception e) {
				e.printStackTrace();
			} 
            method = post;
        }
        else {
            method = new HttpGet(uri);
        }
        return method;
    }

    protected HttpUriRequest createMethod(String httpMethod, URI uri,JSONObject jsonobject,Map<String, String> map) throws HttpException {
        HttpUriRequest method;
        
        //HttpPost
        if (httpMethod.equalsIgnoreCase(HttpPost.METHOD_NAME)) {
            HttpPost post = new HttpPost(uri);
            HttpEntity entity = null;
            
            try {
            	if(jsonobject!=null){
            		StringEntity se = new StringEntity(jsonobject.toString(),UTF8);            		
            		se.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
            	    se.setContentEncoding(new BasicHeader(HTTP.CONTENT_ENCODING, HTTP.UTF_8));
            		post.setEntity(se);
            	}
            } catch (IOException ioe) {
                throw new HttpException(ioe.getMessage(), ioe);
            }
            
            if(map!=null){
            	ArrayList<BasicNameValuePair> postData = new ArrayList<BasicNameValuePair>();
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    postData.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
                try {
                	entity = new UrlEncodedFormEntity(postData, HTTP.UTF_8);
					post.setEntity(entity);
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				}
                
            }
            
            method = post;
        }
        //HttpDelete
        else if (httpMethod.equalsIgnoreCase(HttpDelete.METHOD_NAME)) {
            method = new HttpDelete(uri);
        }
        //HttpPut
        else if(httpMethod.equals(HttpPut.METHOD_NAME)){
        	HttpPut put = new HttpPut(uri);
            try {
            	if(jsonobject!=null){
            		StringEntity se = new StringEntity(jsonobject.toString(),UTF8);
            		put.setEntity(se);
            	}
            } catch (IOException ioe) {
                throw new HttpException(ioe.getMessage(), ioe);
            }
            method = put;           
        }
        else {
            method = new HttpGet(uri);
            LogUtils.v(TAG, "createMethod:"+method.getMethod());
        }
        return method;
    }
    
    /**
     * @n<b>函数名称</b>     :createMethod
     * @brief   表单方式 上传图片使用
     * @see
     * @since    Ver 1.3.0
     * @param  @param httpMethod
     * @param  @param uri
     * @param  @param entity
     * @param  @return
     * @param  @throws HttpException
     * @return HttpUriRequest
     * @<b>作者</b>          :  daiyf
     * @<b>创建时间</b>      :  2013-12-13下午3:36:29      
    */
    protected HttpUriRequest createMethod(String httpMethod, URI uri, MultipartEntity entity) throws HttpException {
        HttpUriRequest method;
        
        //HttpPost
        if (httpMethod.equalsIgnoreCase(HttpPost.METHOD_NAME)) {
            HttpPost post = new HttpPost(uri); 
			post.setEntity(entity);
            
            method = post;
        }
        //HttpDelete
        else if (httpMethod.equalsIgnoreCase(HttpDelete.METHOD_NAME)) {
            method = new HttpDelete(uri);
        }
        //HttpPut
        else if(httpMethod.equals(HttpPut.METHOD_NAME)){
        	HttpPut put = new HttpPut(uri);           
            method = put;           
        }
        else {
            method = new HttpGet(uri);
            LogUtils.v(TAG, "createMethod:"+method.getMethod());
        }
        return method;
    }    
    
    protected void handleResponseStatusCode(int statusCode, Response res) throws HttpAuthException,HttpServerException, HttpException {
        switch (statusCode) {
        case OK:
        case REGISTER_OK:
        case RESULT_OK:
            break;
        case IPNUT_DATA_ERROR:   
        case METHOND_NOT_FOUND:        
            throw new HttpException(res.asString(), statusCode);            
        case NOT_AUTHORIZED:
        case AUTH_ERROR:
        case ERROR_404:
            throw new HttpAuthException(res.asString(), statusCode);
        case INTERFACE_NOT_IMPLEMENTS:
            throw new HttpServerException(res.asString(), statusCode);
        default:
            throw new HttpException(res.asString(), statusCode);
        }
    }

    public void setSid(String sid) {
		this.sid = sid;
	}
    
    public String getSid(){
    	return this.sid;
    }

	/**
     * 异常自动恢复处理, 使用HttpRequestRetryHandler接口实现请求的异常恢复
     */
    protected HttpRequestRetryHandler requestRetryHandler = new HttpRequestRetryHandler() {
        // 自定义的恢复策略
        public boolean retryRequest(IOException exception, int executionCount,HttpContext context) {
            // 设置恢复策略，在发生异常时候将自动重试N次
            if (executionCount >= RETRIED_TIME) {
                return false;
            }
            if (exception instanceof NoHttpResponseException) {
                return true;
            }
            if (exception instanceof SSLHandshakeException) {
                return false;
            }
            HttpRequest request = (HttpRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST);
            boolean idempotent = (request instanceof HttpEntityEnclosingRequest);
             if (!idempotent) {
                return true;
            }
            return false;
        }
    };
    
    
    public Response httpUpImageRequest(String url, Bitmap f0, String httpMethod) 
    					throws HttpAuthException,HttpServerException, HttpException   {    	
		if (f0 == null || f0.isRecycled()) {
			return null;
		}
    	
		Charset charset = Charset.forName("UTF-8");
		MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE, null, charset);
		try {
			entity.addPart("offset", new StringBody(String.valueOf(0)));
			/**
			 * 1、getRowBytes：Since API Level 1，用于计算位图每一行所占用的内存字节数。
			 * 2、getByteCount：Since API Level 12，用于计算位图所占用的内存字节数。
			 * 查看源码实际上 getByteCount() = getRowBytes() * getHeight()，也就是说位图所占用的内存空间数等于位图的每一行所占用的空间数乘以位图的行数
			 */
			entity.addPart("filesize", new StringBody(String.valueOf(f0.getRowBytes() * f0.getHeight())));
		
			ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
			f0.compress(CompressFormat.JPEG, 80, bos);
			ByteArrayBody body = new ByteArrayBody(bos.toByteArray(), "tmp.jpg");
			byte[] data = bos.toByteArray();
			String md5 = MD5ArithmeticUtils.getMD5OfBytes(data);
			entity.addPart("md5",  new StringBody(md5));	
			try {
				bos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			entity.addPart("photo", body);		
		} catch (UnsupportedEncodingException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
    	URI uri = createURI(url);
//        Log.d(TAG, "Sending1 " + httpMethod + " request to " + url);
        HttpResponse response = null;
        Response res = null;
        HttpUriRequest method = null;
        method = createMethod(httpMethod, uri, entity);
//        method.setEntity(entity);
        setupHTTPConnectionParams(method);
        try {
            response = mClient.execute(method,localcontext);    
            res = new Response(response);
            
        } catch (SocketTimeoutException nte) {
            throw new HttpException(nte.getMessage(), HttpStatus.SC_CONFLICT);
        }catch (ClientProtocolException e) {
            throw new HttpException(e.getMessage(), e);
        } catch (IOException ioe) {
            throw new HttpException(ioe.getMessage(), ioe);
        }
        catch(RuntimeException e){
        	LogUtils.e(TAG," httpRequest throw runtimeException");
        }
        if (response != null) {
            int statusCode = response.getStatusLine().getStatusCode();
            handleResponseStatusCode(statusCode, res);
        } else {
            throw new HttpException("response is null");
        }        
        return res;
    }   
    
    /**
     * 解析HTTP错误码
     */
    public static String getCause(int statusCode) {
        String cause = null;
        switch (statusCode) {
        case NOT_RESULT:
        	cause = ApplicationVariable.INSTANCE.applicationContext.getResources().getString(R.string.service_success_no_data);
            break;
        case IPNUT_DATA_ERROR:
            cause = ApplicationVariable.INSTANCE.applicationContext.getResources().getString(R.string.json_error_400);
            break;
        case AUTH_ERROR:
        case ERROR_404:
        case NOT_AUTHORIZED:
            cause = ApplicationVariable.INSTANCE.applicationContext.getResources().getString(R.string.no_request_auth);
            break;
        case METHOND_NOT_FOUND:
        	cause = ApplicationVariable.INSTANCE.applicationContext.getResources().getString(R.string.no_request_method);
        	break;
        case INTERFACE_NOT_IMPLEMENTS:
        	cause = ApplicationVariable.INSTANCE.applicationContext.getResources().getString(R.string.no_impl);
        	break;
        default:
            cause = "";
        }
        return statusCode + ":" + cause;
    }
    
}